#include "loccache.h"

ZEND_DECLARE_MODULE_GLOBALS(loccache);


// globals constructor
static void php_loccache_ctor(zend_loccache_globals *g TSRMLS_DC)
{
	zend_hash_init(&LOCCACHE_G(ht), 64, NULL, ZVAL_PTR_DTOR, 1);
}


// Internal module routinces
PHP_MINIT_FUNCTION(loccache)
{
#ifdef ZTS
	ts_allocate_id(
		&loccache_globals_id,
		sizeof(zend_loccache_globals),
		(ts_allocate_ctor) php_loccache_ctor,
		NULL//(ts_allocate_dtor) php_loccache_dtor
	);
#else
	php_loccache_ctor(&loccache_globals TSRMLS_CC);
#endif

	return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(loccache)
{
#ifndef ZTS
	php_loccache_dtor(&loccache_globals TSRMLS_CC);
#endif

	return SUCCESS;
}

PHP_MINFO_FUNCTION(loccache)
{
	php_info_print_table_start();
	php_info_print_table_row(2, "LocCache Module", "enabled");
	php_info_print_table_row(2, "version", LOCCACHE_EXT_VER);
	php_info_print_table_end();
}



// private
HashTable* getNamespaceHash(HashTable *ht, char *ns_name, size_t ns_len)
{
	HashTable **res;

	if (FAILURE == zend_hash_find(ht, ns_name, ns_len + 1, (void**)&res)) {
		return NULL;
	}

	return *res;
}



// Userspace functions
PHP_FUNCTION(loc_set)
{
	char *ns;
	size_t ns_len;

	char *key;
	size_t key_len;

	zval *val;
	zval *p_val;

	HashTable *g_ht = &LOCCACHE_G(ht);
	HashTable *ns_ht = NULL;

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz", &ns, &ns_len, &key, &key_len, &val)) {
		RETURN_NULL();
	}

	// create namespace
	if (NULL == (ns_ht = getNamespaceHash(g_ht, ns, ns_len))) {
		ns_ht = pemalloc(sizeof(HashTable), 1);
		zend_hash_init(ns_ht, 64, NULL, NULL, 1);

		zend_hash_add(g_ht, ns, ns_len + 1, &ns_ht, sizeof(HashTable *), NULL);
	}

	p_val = malloc(sizeof(zval));
	*p_val = *val;
	zval_copy_ctor(p_val);

	zend_hash_add(ns_ht, key, key_len + 1, &p_val, sizeof(zval *), NULL);

	RETURN_TRUE;
}

PHP_FUNCTION(loc_get)
{
	char *ns;
	size_t ns_len;

	char *key;
	size_t key_len;

	zval **val;
	zval *retval;

	HashTable *g_ht = &LOCCACHE_G(ht);
	HashTable *ns_ht = NULL;

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ns, &ns_len, &key, &key_len)) {
		php_printf("wrong params :(\n");
		RETURN_NULL();
	}

	if (NULL == (ns_ht = getNamespaceHash(g_ht, ns, ns_len))) {
		php_printf("no such namespace :(\n");
		RETURN_NULL();
	}

	if (FAILURE == zend_hash_find(ns_ht, key, key_len + 1, (void**)&val)) {
		php_printf("failed to get stored :(\n");
		RETURN_NULL();
	}

	php_printf("got %x(%x) with %u references from %x\n", *val, val, (*val)->refcount, ns_ht);

	retval = emalloc(sizeof(zval));
	*retval = **val;
	zval_copy_ctor(retval);

	REPLACE_ZVAL_VALUE(&return_value, retval, 1);
}

PHP_FUNCTION(loc_isset)
{
	char *ns;
	size_t ns_len;

	char *key;
	size_t key_len;

	HashTable *g_ht = &LOCCACHE_G(ht);
	HashTable *ns_ht = NULL;

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ns, &ns_len, &key, &key_len)) {
		RETURN_NULL();
	}

	if (NULL == (ns_ht = getNamespaceHash(g_ht, ns, ns_len))) {
		RETURN_NULL();
	}

	if (zend_hash_exists(ns_ht, key, key_len + 1)) {
		RETURN_TRUE;
	}

	RETURN_FALSE;
}

PHP_FUNCTION(loc_unset)
{
	char *ns;
	size_t ns_len;

	char *key;
	size_t key_len;

	HashTable *g_ht = &LOCCACHE_G(ht);
	HashTable *ns_ht = NULL;

	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &ns, &ns_len, &key, &key_len)) {
		php_printf("wrong params :(\n");
		RETURN_NULL();
	}

	if (NULL == (ns_ht = getNamespaceHash(g_ht, ns, ns_len))) {
		php_printf("no such namespace :(\n");
		RETURN_NULL();
	}


	if (FAILURE == zend_hash_del(ns_ht, key, key_len + 1)) {
		RETURN_FALSE;
	}

	RETURN_TRUE;
}



static function_entry php_loccache_functions[] =
{
	PHP_FE(loc_set, NULL)
	PHP_FE(loc_get, NULL)
	PHP_FE(loc_isset, NULL)
	PHP_FE(loc_unset, NULL)
	{NULL, NULL, NULL}
};

zend_module_entry loccache_module_entry =
{
	STANDARD_MODULE_HEADER,
	LOCCACHE_EXT_NAME,
	php_loccache_functions,
	PHP_MINIT(loccache),
	PHP_MSHUTDOWN(loccache),
	NULL,
	NULL,
	PHP_MINFO(loccache),
	LOCCACHE_EXT_VER,
	STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_LOCCACHE
ZEND_GET_MODULE(loccache)
#endif






